package com.sh.data.engine.domain.util;

import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.sh.data.engine.common.exception.BusinessException;
import com.sh.data.engine.common.util.DateUtil;
import com.sh.data.engine.domain.integration.offline.model.enums.OfflineTaskWriterAndDsType;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 动态变量解析工具类
 *
 * <p>支持两种类型的动态变量语法：
 *
 * <ul>
 *   <li>${expression}
 *   <li>#{expression}
 * </ul>
 *
 * <p>先用自定义方案解析动态变量，否则用spel方案解析动态变量
 *
 * @author wy
 */
@Slf4j
public class VarParserUtil {

    private static final Logger logger = LoggerFactory.getLogger(VarParserUtil.class);

    public static String varPattern = "\\$\\{(.+?)\\}";

    public static String varToDbPattern = "to_db\\[(.+?)\\]";

    public static String systemDate = "system.date";

    protected static Pattern P_VAR = Pattern.compile(varPattern);

    public static final String BDP_SYSTEM_CYCTIME = "bdp.system.cyctime";

    public static final String BDP_SYSTEM_BIZDATE = "bdp.system.bizdate";

    public static final String BDP_SYSTEM_PREMONTH = "bdp.system.premonth";

    public static final String BDP_SYSTEM_CURRMONTH = "bdp.system.currmonth";

    public static final String yearUnit = "y";

    public static final String monthUnit = "M";

    public static final String dayUnit = "d";

    public static final String hourUnit1 = "H";

    public static final String hourUnit2 = "h";

    public static final String minUnit = "m";

    public static final String secUnit = "s";


    /**
     * 变量替换
     *
     * @param content      待替换的内容
     * @param params
     * @param businessTime
     * @return
     */
    // 待替换的内容，格式可以如下：
    // select * from test_02 where id = '${bdp.system.bizdate}'   --> select * from test_02 where id =
    // '2021-05-20'
    // select * from test_02 where id = '${bdp.system.bizdate}' and name = '${name}'  -->  select *
    // from test_02 where id = '2021-05-20' and name = 'zhang'   （设置了params）
    // ${name}   -->   zhang     (设置了params)
    // ${name}   -->   ${name}     (未设置params)
    // ${bdp.system.bizdate}    -->   2021-05-20
    public static String process(String content, Map<String, String> params, Date businessTime) {
        if (StringUtils.isBlank(content)) {
            return content;
        }

        if (businessTime == null) {
            businessTime = new Date();
        }

        // 替换用户自定义参数
        if (null != params && !params.isEmpty()) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                String pname = entry.getKey(), pvalue = entry.getValue();
                String regex = "[\\$]\\{\\s*" + pname + "\\s*\\}";
                content = content.replaceAll(regex, pvalue);
            }
        }

        // 处理系统参数（时间参数）
        Matcher m = P_VAR.matcher(content);
        while (m.find()) {
            String slice = m.group();
            String expression = StringUtils.trim(m.group(1));
            try {
                String result = processDateVar(expression, businessTime);
                content = StringUtils.replace(content, slice, result);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
        return content;
    }

    /**
     * dataApi替换变量用
     *
     * @param content
     * @param params
     * @param emptyObject
     * @return
     */
    public static String processForDataApi(
        String content, Map<String, Object> params, Object emptyObject) {
        if (StringUtils.isBlank(content)) {
            return content;
        }

        for (Map.Entry<String, Object> entry : params.entrySet()) {
            Object paramValue = entry.getValue();
            String pname = entry.getKey();

            if (paramValue == null || paramValue == emptyObject) {
                paramValue = "null";
            }

            String regex = "(\\\\)?\\$\\{\\s*(" + pname + ")\\s*\\}";

            Pattern pattern = Pattern.compile(regex);

            Matcher matcher = pattern.matcher(content);

            StringBuffer sb = new StringBuffer();

            while (matcher.find()) {
                String group0 = matcher.group(0);
                String group1 = matcher.group(1);

                sb.append(content, 0, content.indexOf(group0));
                content = content.substring(content.indexOf(group0) + group0.length());

                if (null == group1) {
                    // replace collection default toString()
                    if (paramValue instanceof Collection) {
                        String tmpParamValue = StrUtil.join(",", paramValue);
                        sb.append(tmpParamValue);
                    } else {
                        sb.append(paramValue);
                    }
                } else {
                    sb.append("${" + pname + "}");
                }
            }

            sb.append(content);

            content = sb.toString();
        }

        // 处理系统参数（时间参数）
        Matcher m = P_VAR.matcher(content);
        while (m.find()) {
            String slice = m.group();
            String expression = StringUtils.trim(m.group(1));
            try {
                String result = processDateVar(expression, new Date());
                content = StringUtils.replace(content, slice, result);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
        return content;
    }

    /**
     * apiTask替换变量用
     *
     * @param content
     * @return
     */
    public static String processForApiTaskTest(String content) {
        if (StringUtils.isBlank(content)) {
            return content;
        }

        Pattern pattern = Pattern.compile(varPattern);

        Matcher matcher = pattern.matcher(content);

        while (matcher.find()) {
            String var = matcher.group();
            String value = process(var, null, null);
            if (value.contains("=")) {
                String[] split = value.split("=");
                if (split.length == 2) {
                    String key = split[0];
                    String v = split[1];
                    if ("pageNo".equals(key) || "pageSize".equals(key)) {
                        value = v;
                    }
                }
            }

            content = content.replace(var, value);
        }

        return content;
    }

    /**
     * apiTask替换变量用
     *
     * @param content
     * @return
     */
    public static String processForApiTask(String content) {
        if (StringUtils.isBlank(content)) {
            return content;
        }

        Pattern pattern = Pattern.compile(varPattern);

        Matcher matcher = pattern.matcher(content);

        while (matcher.find()) {
            String var = matcher.group();
            String value = process(var, null, null);
            if (value.contains("=")) {
                String[] split = value.split("=");
                if (split.length == 2) {
                    String key = split[0];
                    if ("pageNo".equals(key) || "pageSize".equals(key)) {
                        continue;
                    }
                }
            }

            content = content.replace(var, value);
        }

        return content;
    }

    public static String processDateVar(String expression, Date businessTime) {
        if (StringUtils.isBlank(expression)) {
            return StringUtils.EMPTY;
        }

        // 处理bdp.system格式的时间
        if (StringUtils.containsIgnoreCase(expression, "bdp.system")) {
            String[] exps = expression.split(",");
            String var = StringUtils.trim(exps[0]);

            // 计算日期加减
            String unit = "d";
            int amount = 0;
            if (var.contains("-")) {
                String[] vars = var.split("-");
                var = StringUtils.trim(vars[0]).toLowerCase();
                String per = StringUtils.trim(vars[1]);
                unit = getUnit(per);
                amount = -getAmount(per);
            } else if (var.contains("+")) {
                String[] vars = var.split("\\+");
                var = StringUtils.trim(vars[0]).toLowerCase();
                String per = StringUtils.trim(vars[1]);
                unit = getUnit(per);
                amount = getAmount(per);
            } else {
                var = StringUtils.trim(var).toLowerCase();
            }

            // 获取时间格式
            String format = null;
            if (exps.length > 1) {
                format = StringUtils.trim(exps[1].replace("'", ""));
            }
            if (var.equalsIgnoreCase(BDP_SYSTEM_CYCTIME)) {
                if (StringUtils.isBlank(format)) {
                    format = "yyyy-MM-dd HH:mm:ss";
                }
            } else if (var.equalsIgnoreCase(BDP_SYSTEM_BIZDATE)) {
                if (StringUtils.isBlank(format)) {
                    format = "yyyy-MM-dd";
                }
            } else if (var.equalsIgnoreCase(BDP_SYSTEM_PREMONTH)) {
                if (StringUtils.isBlank(format)) {
                    format = "yyyy-MM";
                }
                unit = "M";
                amount = -1;
            } else if (var.equalsIgnoreCase(BDP_SYSTEM_CURRMONTH)) {
                if (StringUtils.isBlank(format)) {
                    format = "yyyy-MM";
                }
            }

            businessTime = DateUtil.getPerDate(businessTime, unit, amount);

            String time = DateUtil.formatDate(businessTime, format);

            return time;
        }

        // 处理system.date格式的时候
        String key = "@var@";
        Node node = doParseSystemTime(key, expression);
        return node.getValue();
    }

    private static int getAmount(String per) {
        per =
            per.replace(yearUnit, "")
                .replace(monthUnit, "")
                .replace(dayUnit, "")
                .replace(hourUnit1, "")
                .replace(hourUnit2, "")
                .replace(minUnit, "")
                .replace(secUnit, "");
        if (!NumberUtils.isNumber(per)) {
            throw new BusinessException("日期加减必须为数字");
        }
        return Integer.valueOf(per);
    }

    private static String getUnit(String per) {
        if (per.contains(yearUnit)) {
            return yearUnit;
        } else if (per.contains(monthUnit)) {
            return monthUnit;
        } else if (per.contains(dayUnit)) {
            return dayUnit;
        } else if (per.contains(hourUnit1)) {
            return hourUnit1;
        } else if (per.contains(hourUnit2)) {
            return hourUnit1;
        } else if (per.contains(minUnit)) {
            return minUnit;
        } else if (per.contains(secUnit)) {
            return secUnit;
        }
        throw new BusinessException("单位错误");
    }

    /**
     * 处理datax的where条件变量
     *
     * @param where
     * @param name
     * @param jdbcUrl
     * @param username
     * @param password
     * @param krb5Conf
     * @param keyTabPath
     * @return
     */
    public static String getWhere(
        String where,
        String name,
        String jdbcUrl,
        String username,
        String password,
        String krb5Conf,
        String keyTabPath) {

        Pattern pattern = Pattern.compile(varPattern);

        Matcher matcher = pattern.matcher(where);
        String condition = where;

        while (matcher.find()) {
            String key = "@var@";
            String value = matcher.group();
            value = value.replaceFirst("\\$\\{", "");
            value = value.replaceFirst("\\}", "");
            log.info("############ 检查DataX的where条件 ###############");
            log.info(condition);
            log.info(varPattern);
            log.info(key);
            condition = condition.replaceFirst(varPattern, key);
            Node node = doParse(key, value.trim());

            condition =
                processWhere(
                    condition,
                    node,
                    OfflineTaskWriterAndDsType.getDsType(name),
                    jdbcUrl,
                    username,
                    password,
                    krb5Conf,
                    keyTabPath);
        }

        return condition;
    }

    public static void main(String[] args) {
        String sql = "${system.date,yyyy-MM-dd HH:mm:ss,1}";
        String sql2 = "${bdp.system.bizdate}";
        String sql3 = "select * from test_02 where id = '${system.date,yyyy-MM-dd HH:mm:ss,1}'";
        String sql4 = "ab${param}";
        String sql5 = "${bdp.system.cyctime-1d,yyyyMMdd}";
        String sql6 =
            "{\"job\":{\"content\":[{\"reader\":{\"name\":\"apireader\",\"parameter\":{\"contentType\":\"application/json\",\"method\":\"post\",\"raw\":\"{\\r\\n    \\\"key1\\\": \\\"value1\\\",\\r\\n    \\\"key2\\\": \\\"${bdp.system.bizdate}\\\",\\r\\n    \\\"key3\\\": \\\"${system.date}\\\",\\r\\n    \\\"key4\\\":${pageNo=1}\\r\\n}\",\"requestParams\":[{\"name\":\"k1\",\"position\":\"head\",\"value\":\"v1\"},{\"name\":\"k2\",\"position\":\"query\",\"value\":\"v2\"},{\"name\":\"k3\",\"position\":\"head\",\"value\":\"${system.date}\"}],\"responseParams\":[{\"expression\":\"$\",\"name\":\"re1\"}],\"timeout\":3,\"url\":\"http://baidu.com/s\"}},\"writer\":{\"name\":\"hivewriter\",\"parameter\":{\"allColumn\":[\"id\",\"user_id\",\"project_id\",\"task_type\",\"task_name\",\"data_replace_rule_id\",\"error_data_ctr_num\",\"data_from_ds_type\",\"data_to_ds_type\",\"task_status\",\"row_state\",\"create_time\",\"update_time\",\"is_inc\",\"inc_filter\",\"is_sub_task\"],\"allColumnType\":[\"int\",\"bigint\",\"bigint\",\"int\",\"string\",\"int\",\"int\",\"string\",\"string\",\"int\",\"int\",\"timestamp\",\"timestamp\",\"int\",\"string\",\"int\"],\"batchSize\":20000,\"column\":[\"id\"],\"connection\":[{\"jdbcUrl\":\"jdbc:hive2://10.88.36.232:10000/4360a83b46d2ac67_default\",\"table\":[\"dim_tb_task\"]}],\"dbName\":\"4360a83b46d2ac67_default\",\"hdfsUrl\":\"hdfs://10.88.36.231:8020\",\"hiveMetastoreUrl\":\"thrift://10.88.36.233:9083\",\"hiveType\":0,\"keyTabPath\":\"\",\"krb5Conf\":\"\",\"nameServices\":\"\",\"namenodes\":\"\",\"namenodesAddrs\":\"\",\"password\":\"\",\"patitionValues\":[],\"username\":\"hive\",\"writeMode\":\"overwrite\"}}}],\"setting\":{\"speed\":{\"channel\":1,\"record\":1000}}}}";
        Map<String, String> map = new HashMap<>();
        map.put("name", "zhang");

        System.out.println(processForApiTask(sql6));

        String where = "taskId > ${to_db[select max(id) from tb_task]}";
        where = "create_time> to_data(substr(${to_db[ select coalesce(max(create_time),'1970-01-01 08:00:00') from ODS.sdsd]},1,19),'yyyy-mm-dd)";

        String varPattern = "\\$\\{(.+?)\\}";

        Pattern pattern = Pattern.compile(varPattern);

        Matcher matcher = pattern.matcher(where);
        String condition = where;

        while (matcher.find()) {
            String key = "@var@";
            String value = matcher.group();
            value = value.replaceFirst("\\$\\{", "");
            value = value.replaceFirst("\\}", "");
            log.info("############ 检查DataX的where条件 ###############");
            log.info(condition);
            log.info(varPattern);
            log.info(key);
            condition = condition.replaceFirst(varPattern, key);
            Node node = doParse(key, value.trim());

            condition =
                processWhere(
                    condition,
                    node,
                    OfflineTaskWriterAndDsType.getDsType("MySQL"),
                    "jdbcUrl",
                    "username",
                    "password",
                    "krb5Conf",
                    "keyTabPath");
        }
        System.out.println("condition = " + condition);

        while (matcher.find()) {
            String key = "@var@";
            String value = matcher.group();
            value = value.replaceFirst("\\$\\{", "");
            value = value.replaceFirst("\\}", "");
            condition = condition.replaceFirst(varPattern, key);
            Node node = doParse(key, value.trim());

            System.out.println();
        }

        String pt = "${system.date,yyyy-MM-dd}";
        String pt2 = "${bdp.system.cyctime,yyyy-MM-dd}";


        final String process = process(pt2, null, null);
        System.out.println(process);
    }

    private static String processWhere(
        String where,
        Node node,
        String dsType,
        String jdbcUrl,
        String username,
        String password,
        String krb5Conf,
        String keyTabPath) {

        if (node.getType() == 0) {
            String value = null;
            try {
                //TODO 需要修改
                value =
                    JDBCUtil.getWhere(
                        dsType, jdbcUrl, node.getValue(), username, password, keyTabPath, krb5Conf);

            } catch (BusinessException e) {
                log.error(e.getMessage(), e);
                throw e;
            } catch (Exception e) {
                log.error("增量同步，获取增量标识数值错误");
                throw new BusinessException("增量同步，获取增量标识数值错误");
            }

            if (StringUtils.isBlank(value)) {
                return null;

            } else {
                where = where.replace(node.getKey(), value);
            }

        } else {

            where = where.replace(node.getKey(), node.getValue());
        }

        return where;
    }

    private static Node doParse(String key, String value) {

        Node node = new Node();
        node.setKey(key);
        // 处理to_db关键字
        Pattern pattern0 = Pattern.compile(varToDbPattern);
        Matcher matcher0 = pattern0.matcher(value);
        while (matcher0.find()) {
            String val0 = matcher0.group();
            val0 = val0.replaceFirst("to_db\\[", "");
            val0 = val0.replaceFirst("\\]", "");
            node.setValue(val0);
            node.setType(0);
            return node;
        }

        return doParseSystemTime(key, value);
    }

    private static Node doParseSystemTime(String key, String value) {

        Node node = new Node();
        node.setKey(key);

        // 系统变量
        if (value.trim().startsWith("system.")) {
            String[] vals = value.split(",");
            String val1 = vals[0];
            node.setType(1);
            // 时间变量
            if (val1.contains(systemDate)) {
                String format = "yyyy-MM-dd";
                if (vals.length > 1) {
                    format = vals[1];
                }
                Date date = new Date();
                if (vals.length == 3) {
                    date = DateUtil.getPerDate(date, "d", Integer.parseInt(vals[2].trim()));
                }
                String val2 = DateUtil.formatDate(date, format);

                node.setValue(val2);
            }
        } else {
            node.setType(2);
            node.setValue(value);
        }

        return node;
    }

    public static String getDataqualityPt(String varsStr) {

        String[] vars = varsStr.split(" and");

        Pattern pattern = Pattern.compile(varPattern);
        List<String> wheres = Lists.newArrayList();
        for (String var : vars) {
            var = var.trim();
            Matcher matcher = pattern.matcher(var);
            if (!matcher.find()) {
                String[] kvs = var.split("=");
                var = kvs[0] + "='" + kvs[1] + "'";
                wheres.add(var);
            } else {
                String key = "@var@";
                String value = matcher.group();
                String replace = value;
                value = value.replaceFirst("\\$\\{", "");
                value = value.replaceFirst("\\}", "");
                Node node = doParseSystemTime(key, value.trim());
                value = var.replace(replace, "'" + node.getValue() + "'");
                wheres.add(value);
            }
        }

        return StringUtils.join(wheres, " and ");
    }

    @Data
    private static class Node {

        private String key;

        private String value;

        /**
         * type 0-to_db 1-系统 2-自定义
         */
        private int type;
    }
}
